#include "SMTPSessionManager.h"
#include "Constants.h"

#include <sstream>
#include <boost/regex.hpp>
#include <log4cpp/Category.hh>

namespace SMTP {

log4cpp::Category& logger =
    log4cpp::Category::getInstance(std::string(SERVER_LOGGER));

const std::string mail    = "<[A-Za-z0-9]+@[A-Za-z0-9]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z0-9]+)>.*$";
const std::string SMTP_OK = "250 OK\n";
const std::string SMTP_SYNTAX_ERR = "555 5.5.2 Syntax error.\n";
const std::string SMTP_DATA_OK = "354  Go ahead\n";
const std::string SMTP_MSG_SAVED = "250 2.6.0 message received\n";
const std::string SMTP_BYE = "221 goodbye\n";

SMTPSessionManager::SMTPSessionManager():m_msgCount(0),
    _currState(GREET), _activeMsg(nullptr)  {

}

std::string SMTPSessionManager::greetMessage() {
  _currState=EHLO;
  std::stringstream ss;

  //TODO get this one move to file
  ss<<"*********************************\n";
  ss<<"     Welcome to SMTP Server\n";
  ss<<"*********************************\n";
  ss<<"220 Myhost\n";
  return ss.str();
}

std::string SMTPSessionManager::processCommand(const std::string& inData){

  //MsgPtr msg(new Message());
  std::string data(inData);

  //convert to upper
  for_each(data.begin(), data.end(), [](char& in){ in = ::toupper(in);});

  switch(_currState){
    case GREET:
    {
      _currState=EHLO;
      return "250 Welcome to SMTP Server";
      break;
    }
    case EHLO:
    {
      std::stringstream ss;
      ss<<250<<" Hello " << data;
      _currState=MAIL;
      _activeMsg.reset(new Message());
      return ss.str();
      //parse EHLO
    }
    case MAIL:
    {
      if(processMailFrom(data)){
        _currState=RCPT;
        return SMTP_OK;
      }
      else {
        return SMTP_SYNTAX_ERR;
      }
    }
    case RCPT:
    {
      if(processRcptTo(data)){
        _currState=RCPT;
        return SMTP_OK;
      }
    }
    case DATA:
    {
      //Have we at least one rcpt to
      if (_activeMsg && !_activeMsg->anyRcpt())
        return SMTP_SYNTAX_ERR;
      else {
        //otherwise check for data
        if(data.find("DATA") != std::string::npos) {
          //good to process DATA
          _currState=BODY;
          return SMTP_DATA_OK;
        }
      }
    }
    case BODY:
    {
      if (_activeMsg && !_activeMsg->done()) {
        _activeMsg->addData(data);

        if (_activeMsg->done()) {
          //store message
          _activeMsg->save();
          _msgColl.push_back(std::move(_activeMsg));
          return SMTP_MSG_SAVED;
        }
      }
    }
    case QUIT:
    {
      if(data.find("QUIT") != std::string::npos) {
        return SMTP_BYE;
      }
    }
    default: {

    }

  }

  return "";
}


bool SMTPSessionManager::processMailFrom(const std::string& data){

  std::string mail_from = "^MAIL FROM:";
  mail_from += mail;

  try {
    boost::regex self_regex(mail_from);
    boost::smatch result;

    if (boost::regex_match(data, result, self_regex)) {
      std::string::size_type sn = data.find("<");
      std::string::size_type en = data.rfind(">");
      std::string mailF = data.substr(sn+1, en-1);
      if (_activeMsg) _activeMsg->from(mailF);
      return true;
    }
    else {
      return false;
    }
  }
  catch (const std::exception& e) {
    LOG_ERROR(e.what());
  }

  return false;
}

bool SMTPSessionManager::processRcptTo(const std::string& data){

  std::string rcpt = "^RCPT( TO)*:";
  rcpt += mail;

  try {
    boost::regex self_regex(rcpt);
    if (boost::regex_match(data, self_regex)) {
      std::string::size_type sn = data.find("<");
      std::string::size_type en = data.rfind(">");
      std::string mailF = data.substr(sn, en);
      if (_activeMsg) _activeMsg->rcpt(mailF);
      return true;
    }
    else {
      return false;
    }
  }
  catch(const std::exception& e) {

    LOG_ERROR(e.what());
  }
  return false;
}

}
